#include <stdlib.h>
#include <string.h>

#include <assert.h>
#include <mem.h>
#include "hash.h"

hash* hash_create( uint32 _size, get_hash _get, compare _key_cmp )
{
    hash* _sh;

    _sh = (hash*) palloc( sizeof(hash) );
    assert( _sh != NULL );

    _sh->val = (hash_node*)malloc( sizeof(hash_node)*_size );
    assert( _sh->val != NULL );

    memset( _sh->val, 0, sizeof(hash_node)*_size );
    _sh->bound = _size;
	_sh->size = 0;
	_sh->get = _get;
	_sh->cmp = _key_cmp;
    
    return _sh;
}

void hash_destroy( hash* _sh )
{
    int32 i;
    hash_node *p, *pn;
    assert( _sh != NULL );
    assert( _sh->val != NULL );

    for( i = 0; i < _sh->bound; ++i )
    {
        p = _sh->val[i].next;
        while( p != NULL )
        {
            pn = p->next;
            free( p );
            p = pn;
        }
    }

    free( _sh->val );
    _sh->val = NULL;
    _sh->size = 0;
    free( _sh );
}

void hash_put( hash* _sh, void *_key, void* _val )
{
    hash_node *n;
    int32 _k;
    
    _k = (_sh->get(_key)%_sh->bound + 13) % _sh->bound;

    if( _sh->val[_k].key == NULL ||
		0 == _sh->cmp( _sh->val[_k].key, _key ) )
    {
        _sh->val[_k].val = _val;
        _sh->val[_k].key = _key;
    }
    else
    {

		n = _sh->val+_k;
		while( n->next )
		{
			if( 0 == _sh->cmp(n->next->key,_key) )
			{
				n->next->val = _val;
				n->next->key = _key;
				break;
			}
			n = n->next;
		}
		if( n->next == NULL )
		{
			n->next = (hash_node*)malloc( sizeof(hash_node) );
			assert( n->next != NULL );

			n->next->key = _key;
			n->next->val = _val;
		}
    }

}

void* hash_get( hash* _sh, void *_key )
{
    hash_node *pn;
    int _k;
    void* v = NULL;

    _k = (_sh->get(_key)%_sh->bound + 13) % _sh->bound;
    pn = _sh->val + _k;
    while( pn )
    {
        if( 0 == _sh->cmp( pn->key, _key) )
        {
            v = pn->val;
            break;
        }
        pn = pn->next;
    }

    return v;
}

void* hash_del( hash* _sh, void *_key )
{
    hash_node *pn, *p;
    int _k;
    void* v = NULL;
    
    _k = (_sh->get(_key)%_sh->size + 13) % _sh->size;
    pn = _sh->val + _k;
    if( 0 == _sh->cmp( pn->key, _key ) )
    {
        v = pn->val;
        pn->key = 0;
        pn->val = NULL;
        return v;
    }
    while( pn->next )
    {
        if( 0 == _sh->cmp( pn->next->key, _key ) )
        {
            v = pn->next->val;
            p = pn->next;
            pn->next = p->next;
            pfree( p );
            break;
        }
        pn = pn->next;
    }
    
    return v;
}
